package com.hsurosy.hsuchain.nft.datasource.sharding.alogorithm;

import com.hsurosy.hsuchain.nft.datasource.sharding.id.DistributeID;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.shardingsphere.sharding.api.sharding.complex.ComplexKeysShardingAlgorithm;
import org.apache.shardingsphere.sharding.api.sharding.complex.ComplexKeysShardingValue;
import org.apache.shardingsphere.sharding.api.sharding.hint.HintShardingAlgorithm;
import org.apache.shardingsphere.sharding.api.sharding.hint.HintShardingValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collection;
import java.util.HashSet;
import java.util.Properties;
import java.util.stream.Collectors;

/**
 * 复合键和 Hint 分片算法的实现
 *
 * @Author Hsu琛君珩
 * @Date 2024-10-04 19:15
 * @Description
 * @Version: v1.0.0
 */
public class TurboKeyShardingAlgorithm implements ComplexKeysShardingAlgorithm<String>, HintShardingAlgorithm<String> {

    // 日志记录器
    private static Logger logger = LoggerFactory.getLogger(TurboKeyShardingAlgorithm.class);

    // 配置属性
    private Properties props;

    // 主分片列配置属性名称
    private static final String PROP_MAIN_COLUM = "mainColum";

    // 表数量配置属性名称
    private static final String PROP_TABLE_COUNT = "tableCount";

    // 获取配置属性
    @Override
    public Properties getProps() {
        return props;
    }

    // 初始化时设置配置属性
    @Override
    public void init(Properties props) {
        this.props = props;
    }

    /**
     * 复合键分片算法的实现
     *
     * @param availableTargetNames 可供选择的目标分片表名集合
     * @param complexKeysShardingValue 复合键的分片值
     * @return 匹配到的目标分片表集合
     */
    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames, ComplexKeysShardingValue<String> complexKeysShardingValue) {
        Collection<String> result = new HashSet<>();

        // 获取主分片列
        String mainColum = props.getProperty(PROP_MAIN_COLUM);
        Collection<String> mainColums = complexKeysShardingValue.getColumnNameAndShardingValuesMap().get(mainColum);

        // 使用主分片列进行分片
        if (CollectionUtils.isNotEmpty(mainColums)) {
            for (String colum : mainColums) {
                String shardingTarget = calculateShardingTarget(colum);
                result.add(shardingTarget);
            }
            return getMatchedTables(result, availableTargetNames);
        }

        // 如果主分片列为空，使用其他分片列进行分片
        complexKeysShardingValue.getColumnNameAndShardingValuesMap().remove(mainColum);
        Collection<String> otherColums = complexKeysShardingValue.getColumnNameAndShardingValuesMap().keySet();
        if (CollectionUtils.isNotEmpty(otherColums)) {
            for (String colum : otherColums) {
                Collection<String> otherColumValues = complexKeysShardingValue.getColumnNameAndShardingValuesMap().get(colum);
                for (String value : otherColumValues) {
                    String shardingTarget = extractShardingTarget(value);
                    result.add(shardingTarget);
                }
            }
            return getMatchedTables(result, availableTargetNames);
        }

        return null;
    }

    // 获取匹配的目标表
    private Collection<String> getMatchedTables(Collection<String> results, Collection<String> availableTargetNames) {
        Collection<String> matchedTables = new HashSet<>();
        for (String result : results) {
            matchedTables.addAll(availableTargetNames.parallelStream().filter(each -> each.endsWith(result)).collect(Collectors.toSet()));
        }
        return matchedTables;
    }

    // 从订单ID中提取目标表的分片信息
    private String extractShardingTarget(String orderId) {
        return DistributeID.getShardingTable(orderId);
    }

    // 根据买家ID计算分片目标表
    private String calculateShardingTarget(String buyerId) {
        String tableCount = props.getProperty(PROP_TABLE_COUNT);
        return DistributeID.getShardingTable(buyerId, Integer.parseInt(tableCount));
    }

    /**
     * Hint 分片算法的实现
     *
     * @param collection 可供选择的分片表集合
     * @param hintShardingValue 包含 Hint 信息的分片值
     * @return 匹配到的目标分片表集合
     */
    @Override
    public Collection<String> doSharding(Collection<String> collection, HintShardingValue<String> hintShardingValue) {
        logger.info("collection : " + collection);
        logger.info("hintShardingValue : " + hintShardingValue);
        String logicTableName = hintShardingValue.getLogicTableName();
        Collection<String> shardingTargets = hintShardingValue.getValues();

        Collection<String> matchedTables = new HashSet<>();
        for (String shardingTarget : shardingTargets) {
            matchedTables.add(logicTableName + "_" + shardingTarget);
        }

        logger.info("matchedTables : " + matchedTables);
        return CollectionUtils.intersection(collection, matchedTables);
    }
}
